package com.vision.config;

import com.alibaba.fastjson2.JSON;
import com.vision.pojo.entity.Message;
import com.vision.pojo.vo.MessageVO;
import com.vision.service.MessageService;
import com.vision.utils.MessageUtil;
import jakarta.servlet.http.HttpSession;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
@ServerEndpoint(value = "/websocket", configurator = GetUserInfo.class)
public class WebSocketServer {

    /**
     * 用于存放在线用户，且为线程安全
     */
    public static final Map<String,Session> onLineUsers = new ConcurrentHashMap<>();
    /**
     * 存放有离线消息的用户发送消息的用户 id
     */
    private static final Map<String, List<Integer>> offLineUsers = new ConcurrentHashMap<>();

    private HttpSession httpSession;

    /**
     * 注入 MessageService
     */
    private static MessageService messageService;
    @Autowired
    public void setStringRedisTemplate(MessageService messageService) {
        WebSocketServer.messageService = messageService;
    }

    /**
     * 发送消息
     * @param messageVO 消息封装对象
     */
    public static void sendMessage (MessageVO messageVO) {
        try {
            // 1. 获取接收方的 session
            Session targetSession = onLineUsers.get(messageVO.getReceiverUsername());
            // 2. 对 session 进行判空，如果 session 不存在则说明用户不在线
            if (targetSession == null) {
                throw new RuntimeException("用户不在线");
            }
            // 3. 将消息发送给接收方
            targetSession.getBasicRemote().sendText(JSON.toJSONString(messageVO));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 建立 websocket 连接后，调用该方法
     * @param session 会话
     * @param config 配置
     */
    @OnOpen
    public void onOpen (Session session, EndpointConfig config) throws IOException {
        // 解析 session 中的 claims 获取当前用户的用户名
        this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());
        // 获取当前用户的信息
        Map<String,Object> claims = (Map<String,Object>) this.httpSession.getAttribute("claims");
        String username = (String)claims.get("username");// 获取 username

        // 如果 username 为空，说明用户未登录，直接返回
        if (username == null) {
            return;
        }

        // 将当前用户存入在线用户中
        onLineUsers.put(username,session);
        List<Integer> integers = offLineUsers.get(username);
        if (integers != null) {
            session.getBasicRemote().sendText(JSON.toJSONString(integers));
            offLineUsers.remove(username);
        }
        System.out.println("连接成功");
    }

    /**
     * 收到消息后，调用该方法
     * @param msg 消息
     */
    @OnMessage
    public void onMessage (String msg) {
        MessageVO messageVO = null;
        try {
            // 1. 解析 json 对象
            messageVO = JSON.parseObject(msg, MessageVO.class);
            // 2. 获取接收方 username
            String receiverUsername = messageVO.getReceiverUsername();
            // 4. 获取接收方的 session
            Session targetSession = onLineUsers.get(receiverUsername);
            // 5. 对 session 进行判空，如果 session 不存在则说明用户不在线
            if (targetSession != null) {
                // 6. 将消息发送给接收方
                targetSession.getBasicRemote().sendText(JSON.toJSONString(messageVO));
            } else {
                // 6. 标记该用户有离线消息
                List<Integer> list = offLineUsers.get(receiverUsername);
                if (list == null) {
                    list = new ArrayList<>(List.of(messageVO.getSenderId()));
                } else if(!list.contains(messageVO.getSenderId())) {
                    System.out.println(messageVO.getSenderId());
                    list.add(messageVO.getSenderId());
                }
                offLineUsers.put(receiverUsername,list);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (messageVO != null) {
                // 7. 将消息存入数据库
                Message message = new Message();
                message.setSenderId(messageVO.getSenderId());
                message.setReceiverId(messageVO.getReceiverId());
                message.setContent(messageVO.getContent());
                message.setIsSystem(messageVO.getIsSystem());
                messageService.saveMessage(message);
            }
        }
    }

    /**
     * 关闭连接后，调用该方法
     */
    @OnClose
    public void onClose () {
        // 将当前用户从在线用户中移除
        onLineUsers.remove(getUsername());
        System.out.println("连接关闭");
    }

    /**
     * 发生错误后，调用该方法
     * @param session 会话
     * @param error 错误
     */
    @OnError
    public void onError (Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }

    /**
     * 获取当前用户的用户名
     * @return 当前用户的用户名
     */
    private String getUsername () {
        Map<String,Object> claims = (Map<String,Object>) this.httpSession.getAttribute("claims");
        return (String) claims.get("username");
    }
}
